package cc.mango.dao;

import java.sql.*;
import java.util.*;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import cc.mango.util.Config;
import cc.mango.util.Contant;

/**
 * ProjectName：webServerController Class ConnetionPool Class Detail：
 * Createor：hunter Create Time：Jan 11, 2012 6:00:15 PM Modifier：hunter Modify
 * Date：Jan 11, 2012 6:00:15 PM Modify Detail：
 *
 * @version
 *
 */
public class ConnectionPool
{
	private static LinkedList<ConnectionHandler>	notUsedConnection			= new LinkedList<ConnectionHandler>();
	private static HashSet<ConnectionHandler>	usedUsedConnection			= new HashSet<ConnectionHandler>();
	private static String					url						= "";
	private static String					user						= "";
	private static String					password					= "";
	static private long						lastClearClosedConnection	= System.currentTimeMillis();
	private static final Log					log						= LogFactory
																		.getLog(ConnectionPool.class);
	public static long						CHECK_CLOSED_CONNECTION_TIME	= 3 * 60 * 60 * 1000;					// 4
																									// hours

	private static int						minConnectionCount			= 0;
	private static int						maxConnectionCount			= 0;
	static
	{
		try
		{
			Class.forName(Config.getInstance(
					Config.class
							.getResource(Contant.HOST_CONFIG_INFO_PATH)
							.toString().replace("file:", "")).getString(
					"connection.driver.class", "com.mysql.jdbc.Driver"));
			url = Config.getInstance(
					Config.class
							.getResource(Contant.HOST_CONFIG_INFO_PATH)
							.toString().replace("file:", "")).getString(
					"connection.url", "com.mysql.jdbc.Driver");
			user = Config.getInstance(
					Config.class
							.getResource(Contant.HOST_CONFIG_INFO_PATH)
							.toString().replace("file:", "")).getString(
					"connection.username", "root");
			password = Config.getInstance(
					Config.class
							.getResource(Contant.HOST_CONFIG_INFO_PATH)
							.toString().replace("file:", "")).getString(
					"connection.password", "123456");
			minConnectionCount = Config.getInstance(
					Config.class
							.getResource(Contant.HOST_CONFIG_INFO_PATH)
							.toString().replace("file:", "")).getInt(
					"connection.min.count", 30);
			maxConnectionCount = Config.getInstance(
					Config.class
							.getResource(Contant.HOST_CONFIG_INFO_PATH)
							.toString().replace("file:", "")).getInt(
					"connection.max.count", 200);
			CHECK_CLOSED_CONNECTION_TIME = Config.getInstance(
					Config.class
							.getResource(Contant.HOST_CONFIG_INFO_PATH)
							.toString().replace("file:", "")).getLong(
					"connection.check.close.time", 3 * 60 * 60 * 1000);
		}
		catch (Exception e)
		{

		}
	}

	private ConnectionPool()
	{
	}

	public static synchronized Connection getConnection()
	{
		clearClosedConnection();
		while (notUsedConnection.size() > 0)
		{
			try
			{
				ConnectionHandler connection = (ConnectionHandler) notUsedConnection
						.removeFirst();
				if (connection.getConnection().isClosed())
				{
					continue;
				}
				usedUsedConnection.add(connection);
				log.debug("return a connection");
				return connection.getConnection();
			}
			catch (Exception e)
			{
			}
		}
		int newCount = getAddConnectionCount();
		ConnectionHandler newConnection = null;
		for (int i = 0; i < newCount; i++)
		{
			newConnection = new ConnectionHandler(getNewConnection());
			if (newConnection != null)
			{
				notUsedConnection.add(newConnection);
			}
		}
		newConnection = (ConnectionHandler) notUsedConnection.removeFirst();
		usedUsedConnection.add(newConnection);

		return newConnection.getConnection();
	}

	/**
	 * get new Connection
	 *
	 * @return
	 */
	private static Connection getNewConnection()
	{
		try
		{
			Connection con = DriverManager
					.getConnection(url, user, password);
			return con;
		}
		catch (Exception e)
		{
			e.printStackTrace();
		}
		return null;
	}

	public synchronized static void pushConnectionBackToPool(
			ConnectionHandler con)
	{
		boolean exist = usedUsedConnection.remove(con);
		if (exist)
		{
			notUsedConnection.addLast(con);
		}
		log.debug("not used count:" + notUsedConnection.size()
				+ ", use used count:" + usedUsedConnection.size());
	}

	/**
	 * Close All Connection
	 *
	 * @return
	 */
	public static int closeAllConnection()
	{
		int count = 0;
		Iterator iterator = notUsedConnection.iterator();
		while (iterator.hasNext())
		{
			try
			{
				((ConnectionHandler) iterator.next())
						.getM_originConnection().close();
				count++;
			}
			catch (Exception e)
			{
			}
		}
		notUsedConnection.clear();

		iterator = usedUsedConnection.iterator();
		while (iterator.hasNext())
		{
			try
			{
				ConnectionHandler connection = (ConnectionHandler) iterator
						.next();
				connection.getM_originConnection().close();
				log.debug("");
				count++;
			}
			catch (Exception e)
			{
			}
		}
		usedUsedConnection.clear();

		return count;
	}

	/**
	 * remove not used connection
	 */
	private static void clearClosedConnection()
	{
		long time = System.currentTimeMillis();
		// sometimes user change system time,just return
		if (time < lastClearClosedConnection)
		{
			time = lastClearClosedConnection;
			return;
		}
		// no need check very often
		if (time - lastClearClosedConnection < CHECK_CLOSED_CONNECTION_TIME)
		{
			return;
		}
		lastClearClosedConnection = time;

		// begin check
		Iterator iterator = notUsedConnection.iterator();
		while (iterator.hasNext())
		{
			ConnectionHandler connection = (ConnectionHandler) iterator
					.next();
			try
			{
				if (connection.getConnection().isClosed())
				{
					iterator.remove();
					log.debug("connection is closed, remove not used connection");
				}
			}
			catch (Exception e)
			{
				iterator.remove();
				log.debug("connection is closed, this connection initial StackTrace");
			}
		}

		// make connection pool size smaller if too big
		int decrease = getDeleteConnectionCount();
		if (notUsedConnection.size() < decrease)
		{
			return;
		}

		while (decrease-- > 0)
		{
			ConnectionHandler connection = (ConnectionHandler) notUsedConnection
					.removeFirst();
			try
			{
				connection.getM_originConnection().close();
			}
			catch (Exception e)
			{
			}
		}
	}

	/**
	 * get increasing connection count, not just add 1 connection
	 *
	 * @return count
	 */
	public static int getAddConnectionCount()
	{
		int count = 1;
		int current = getConnectionCount();
		count = current / 5;
		if (count < 1)
		{
			count = 1;
		}
		return count;
	}

	/**
	 * get decreasing connection count, not just remove 1 connection
	 *
	 * @return count
	 */
	public static int getDeleteConnectionCount()
	{
		int current = getConnectionCount();
		if (current < minConnectionCount)
		{
			return 0;
		}
		return current / 4;
	}

	public static synchronized int getNotUsedConnectionCount()
	{
		return notUsedConnection.size();
	}

	public static synchronized int getUsedConnectionCount()
	{
		return usedUsedConnection.size();
	}

	public static synchronized int getConnectionCount()
	{
		return notUsedConnection.size() + usedUsedConnection.size();
	}

	public static void main(String[] args)
	{

		for (int i = 0; i < 2000; i++)
		{
			try
			{
				Thread.sleep(1);
			}
			catch (InterruptedException e1)
			{
				// TODO Auto-generated catch block
				e1.printStackTrace();
			}
			new Thread()
			{
				public void run()
				{
					try
					{

						Connection conn1 = ConnectionPool.getConnection();
						PreparedStatement p = conn1
								.prepareStatement("select * from host");
						ResultSet rs = p.executeQuery();
						while (rs.next())
						{
							log.debug(rs.getObject("Host"));
						}
						rs.close();
						p.close();
						conn1.close();
					}
					catch (SQLException e)
					{
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
			}.start();

		}

	}
}
